package com.ac.core.dao.hibernate;

import java.io.Serializable;
import java.math.BigDecimal;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.Statement;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import com.ac.core.config.OrderClazz;
import com.ac.core.config.QueryClass;
import com.ac.core.dao.BaseDao;
import com.ac.core.dao.CriteriaCallback;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Criteria;
import org.hibernate.FlushMode;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.criterion.Expression;
import org.hibernate.criterion.Junction;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

/**
 * <p>Title: </p>
 * <p>Description: </p>
 * <p>Copyright: Copyright caac 2006</p>
 * <p>Company: caac</p>
 * @author <a href="mailto:project@caac.com.cn">Project Group</a>
 * @version 1.0
 */

public class BaseDaoHibernate extends HibernateDaoSupport implements BaseDao {
    protected final Log log = LogFactory.getLog(getClass());

    public Object getObjectById(Class entityClass, Serializable id) {
        return getHibernateTemplate().get(entityClass, id);
    }

    public Object loadObjectById(Class entityClass, Serializable id) {
        return getHibernateTemplate().load(entityClass, id);
    }

    public List getAllObjects(Class entityClass) {
        return getHibernateTemplate().loadAll(entityClass);
    }

    public void setFlushModeAuto() {
        this.getSession().setFlushMode(FlushMode.AUTO);
    }

    public void saveObject(Object object) {
        getHibernateTemplate().saveOrUpdate(object);
    }

    public void newObject(Object object) {
        getHibernateTemplate().save(object);
    }

    public void deleteObject(String entity) {
        this.getSession().setFlushMode(FlushMode.AUTO);
        getHibernateTemplate().delete("from " + entity);
        try {
            this.getSession().flush();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void deleteObject(Object entity) {
        getHibernateTemplate().delete(entity);
    }

    public void deleteObjectById(Class entityClass, Serializable id) {
        deleteObject(loadObjectById(entityClass, id));
    }

    public void deleteAllObjects(Collection entities) {
        getHibernateTemplate().deleteAll(entities);
    }

    public List getObjects(final Class clazz, final QueryClass qc,
                           final Object bean, final boolean and,
                           final OrderClazz oc, final CriteriaCallback cc,
                           final int from, final int size) {
        return getHibernateTemplate().executeFind(new HibernateCallback() {
            public Object doInHibernate(Session session) throws
                    HibernateException {
                Criteria criteria = session.createCriteria(clazz);
                Map aliasMap = new TreeMap();
                Map propMap = new TreeMap();
                qc.init(bean, aliasMap, propMap);
                oc.init(aliasMap);
                cc.populateAlias(aliasMap);
                createAlias(criteria, aliasMap);
                cc.execute(criteria);
                addJunction(criteria, qc, propMap, and);
                oc.addCriteria(criteria);
                pageCriteria(criteria, from, size);
                return criteria.list();
            }
        });
    }

    public List getObjects(final Class clazz, final int from, final int size,
                           final String orderName) {
        return getHibernateTemplate().executeFind(new HibernateCallback() {
            public Object doInHibernate(Session session) throws
                    HibernateException {
                Criteria criteria = session.createCriteria(clazz);
                pageCriteria(criteria, from, size);
                criteria.addOrder(Order.asc(orderName));
                return criteria.list();
            }
        });
    }

    public List getObjects(final Class clazz, final QueryClass qc,
                           final Object bean, final boolean and,
                           final OrderClazz oc, final int from, final int size) {
        return getHibernateTemplate().executeFind(new HibernateCallback() {
            public Object doInHibernate(Session session) throws
                    HibernateException {
                Criteria criteria = session.createCriteria(clazz);
                Map aliasMap = new TreeMap();
                Map propMap = new TreeMap();
                qc.init(bean, aliasMap, propMap);
                oc.init(aliasMap);
                createAlias(criteria, aliasMap);
                addJunction(criteria, qc, propMap, and);
                oc.addCriteria(criteria);
                pageCriteria(criteria, from, size);
                return criteria.list();
            }
        });
    }

    public List getObjects(final Class clazz, final QueryClass qc,
                           final Object bean, final boolean and,
                           final CriteriaCallback cc, final int from,
                           final int size) {
        return getHibernateTemplate().executeFind(new HibernateCallback() {
            public Object doInHibernate(Session session) throws
                    HibernateException {
                Criteria criteria = session.createCriteria(clazz);
                Map aliasMap = new TreeMap();
                Map propMap = new TreeMap();
                qc.init(bean, aliasMap, propMap);
                cc.populateAlias(aliasMap);
                createAlias(criteria, aliasMap);
                cc.execute(criteria);
                addJunction(criteria, qc, propMap, and);
                pageCriteria(criteria, from, size);
                return criteria.list();
            }
        });
    }

    public List getObjects(final Class clazz, final QueryClass qc,
                           final Object bean, final boolean and, final int from,
                           final int size) {
        return getHibernateTemplate().executeFind(new HibernateCallback() {
            public Object doInHibernate(Session session) throws
                    HibernateException {
                Criteria criteria = session.createCriteria(clazz);
                Map aliasMap = new TreeMap();
                Map propMap = new TreeMap();
                qc.init(bean, aliasMap, propMap);
                createAlias(criteria, aliasMap);
                addJunction(criteria, qc, propMap, and);
                pageCriteria(criteria, from, size);
                return criteria.list();
            }
        });
    }

    public List getObjects(final Class clazz, final QueryClass qc,
                           final Object bean, final boolean and,
                           final OrderClazz oc, final CriteriaCallback cc) {
        return getHibernateTemplate().executeFind(new HibernateCallback() {
            public Object doInHibernate(Session session) throws
                    HibernateException {
                Criteria criteria = session.createCriteria(clazz);
                Map aliasMap = new TreeMap();
                Map propMap = new TreeMap();
                qc.init(bean, aliasMap, propMap);
                oc.init(aliasMap);
                cc.populateAlias(aliasMap);
                createAlias(criteria, aliasMap);
                cc.execute(criteria);
                addJunction(criteria, qc, propMap, and);
                oc.addCriteria(criteria);
                return criteria.list();
            }
        });
    }

    public List getObjects(final Class clazz, final QueryClass qc,
                           final Object bean, final boolean and,
                           final OrderClazz oc) {
        return getHibernateTemplate().executeFind(new HibernateCallback() {
            public Object doInHibernate(Session session) throws
                    HibernateException {
                Criteria criteria = session.createCriteria(clazz);
                Map aliasMap = new TreeMap();
                Map propMap = new TreeMap();
                qc.init(bean, aliasMap, propMap);
                oc.init(aliasMap);
                createAlias(criteria, aliasMap);
                addJunction(criteria, qc, propMap, and);
                oc.addCriteria(criteria);
                return criteria.list();
            }
        });
    }

    public List getObjects(final Class clazz, final QueryClass qc,
                           final Object bean, final boolean and,
                           final CriteriaCallback cc) {
        return getHibernateTemplate().executeFind(new HibernateCallback() {
            public Object doInHibernate(Session session) throws
                    HibernateException {
                Criteria criteria = session.createCriteria(clazz);
                Map aliasMap = new TreeMap();
                Map propMap = new TreeMap();
                qc.init(bean, aliasMap, propMap);
                cc.populateAlias(aliasMap);
                createAlias(criteria, aliasMap);
                cc.execute(criteria);
                addJunction(criteria, qc, propMap, and);
                return criteria.list();
            }
        });
    }

    public List getObjects(final Class clazz, final QueryClass qc,
                           final Object bean, final boolean and) {
        return getHibernateTemplate().executeFind(new HibernateCallback() {
            public Object doInHibernate(Session session) throws
                    HibernateException {
                Criteria criteria = session.createCriteria(clazz);
                Map aliasMap = new TreeMap();
                Map propMap = new TreeMap();
                qc.init(bean, aliasMap, propMap);
                createAlias(criteria, aliasMap);
                addJunction(criteria, qc, propMap, and);
                return criteria.list();
            }
        });
    }

    public List getObjects(final Class clazz, final String whereString,
                           final OrderClazz oc, final int from, final int size) {
        return getHibernateTemplate().executeFind(new HibernateCallback() {
            public Object doInHibernate(Session session) throws
                    HibernateException {
                Criteria criteria = session.createCriteria(clazz).add(
                        Expression.sql(whereString));
                oc.addCriteria(criteria);
                pageCriteria(criteria, from, size);
                return criteria.list();
            }
        });
    }

    public List getObjects(final Class clazz, final String whereString,
                           final OrderClazz oc) {
        return getHibernateTemplate().executeFind(new HibernateCallback() {
            public Object doInHibernate(Session session) throws
                    HibernateException {
                Criteria criteria = session.createCriteria(clazz).add(
                        Expression.sql(whereString));
                oc.addCriteria(criteria);
                return criteria.list();
            }
        });

    }

    public List getObjects(final Class clazz, final String whereString) {
        return getHibernateTemplate().executeFind(new HibernateCallback() {
            public Object doInHibernate(Session session) throws
                    HibernateException {
                Criteria criteria = session.createCriteria(clazz).add(
                        Expression.sql(whereString));
                return criteria.list();
            }
        });
    }

    public List getAllObjects(final Class clazz, final OrderClazz oc) {
        return getHibernateTemplate().executeFind(new HibernateCallback() {
            public Object doInHibernate(Session session) throws
                    HibernateException {
                Criteria criteria = session.createCriteria(clazz);
                Map aliasMap = new TreeMap();
                //Map propMap = new TreeMap();
                oc.init(aliasMap);
                createAlias(criteria, aliasMap);
                oc.addCriteria(criteria);
                return criteria.list();
            }
        });
    }

    public List getObjects(final Class clazz, final String sql, final int count) {
        return getHibernateTemplate().executeFind(new HibernateCallback() {
            public Object doInHibernate(Session session) throws
                    HibernateException {
                return session.createSQLQuery(sql).addEntity("a", clazz)
                        .setMaxResults(count).list();
            }
        });
    }

    public List getObjects(final Class clazz, final String whereString,
                           final int from, final int size) {
        return getHibernateTemplate().executeFind(new HibernateCallback() {
            public Object doInHibernate(Session session) throws
                    HibernateException {
                Criteria criteria = session.createCriteria(clazz).add(
                        Expression.sql(whereString));
                pageCriteria(criteria, from, size);
                return criteria.list();
            }
        });
    }

    public int getCount(final Class clazz, final QueryClass qc,
                        final Object bean, final boolean and,
                        final CriteriaCallback cc) {
        return ((Integer) (getHibernateTemplate().execute(new
                HibernateCallback() {
            public Object doInHibernate(Session session) throws
                    HibernateException {
                Criteria criteria = session.createCriteria(clazz);
                Map aliasMap = new TreeMap();
                Map propMap = new TreeMap();
                qc.init(bean, aliasMap, propMap);
                cc.populateAlias(aliasMap);
                createAlias(criteria, aliasMap);
                cc.execute(criteria);
                addJunction(criteria, qc, propMap, and);
                int totalCount = ((Integer) criteria.setProjection(Projections.
                        rowCount()).uniqueResult()).intValue();
                criteria.setProjection(null);
                return new Integer(totalCount);
            }
        }))).intValue();
    }

    public int getCount(final Class clazz, final QueryClass qc,
                        final Object bean, final boolean and) {
        return ((Integer) (getHibernateTemplate().execute(new
                HibernateCallback() {
            public Object doInHibernate(Session session) throws
                    HibernateException {
                Criteria criteria = session.createCriteria(clazz);
                Map aliasMap = new TreeMap();
                Map propMap = new TreeMap();
                qc.init(bean, aliasMap, propMap);
                createAlias(criteria, aliasMap);
                addJunction(criteria, qc, propMap, and);
                int totalCount = ((Integer) criteria.setProjection(Projections.
                        rowCount()).uniqueResult()).intValue();
                criteria.setProjection(null);
                return new Integer(totalCount);
            }
        }))).intValue();
    }

    public int getCount(final Class clazz, final String whereString) {
        return ((Integer) (getHibernateTemplate().execute(new
                HibernateCallback() {
            public Object doInHibernate(Session session) throws
                    HibernateException {
                Criteria criteria = session.createCriteria(clazz).add(
                        Expression.sql(whereString));
                int totalCount = ((Integer) criteria.setProjection(Projections.
                        rowCount()).uniqueResult()).intValue();
                criteria.setProjection(null);
                return new Integer(totalCount);
            }
        }))).intValue();
    }

    public int getAllCount(final Class clazz) {
        return ((Integer) (getHibernateTemplate().execute(new
                HibernateCallback() {
            public Object doInHibernate(Session session) throws
                    HibernateException {
                Criteria criteria = session.createCriteria(clazz);
                int totalCount = ((Integer) criteria.setProjection(Projections.
                        rowCount()).uniqueResult()).intValue();
                criteria.setProjection(null);
                return new Integer(totalCount);
            }
        }))).intValue();
    }

    private void pageCriteria(Criteria criteria, int from, int size) {
        criteria.setFirstResult(from - 1);
        criteria.setMaxResults(size);
    }

    private void createAlias(Criteria criteria, Map aliasMap) throws
            HibernateException {
        for (Iterator it = aliasMap.keySet().iterator(); it.hasNext(); ) {
            String parentName = (String) it.next();
            String prefix = (String) aliasMap.get(parentName);
            criteria.createAlias(parentName, prefix);
        }
    }

    private void addJunction(Criteria criteria, QueryClass qc, Map propMap,
                             boolean and) {
        Junction junction;
        if (and) {
            junction = Expression.conjunction();
        } else {
            junction = Expression.disjunction();
        }
        qc.addCriterion(junction, propMap);
        criteria.add(junction);
    }

    public void execNoResultProc(String strSql) {
        Session ses = null;
        CallableStatement stmt = null;
        try {
            ses = getSessionFactory().openSession();
            stmt = ses.connection().prepareCall(strSql);
            stmt.execute();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (stmt != null) {
                    stmt.close();
                }
                if (ses != null) {
                    ses.close();
                }
            } catch (Exception e) {}
        }
    }

    public void execNoResultSql(String strSql) {
        Session ses = null;
        Statement stmt = null;
        try {
            ses = getSessionFactory().openSession();
            Transaction tx = ses.beginTransaction();
            stmt = ses.connection().createStatement();
            stmt.execute(strSql);
            tx.commit();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (stmt != null) {
                    stmt.close();
                }
                if (ses != null) {
                    ses.close();
                }
            } catch (Exception e) {}
        }
    }

    public List execSqlWithResult(String strSql) {
        List rstList = new ArrayList();
        Session ses = null;
        Connection con = null;
        Statement stmt = null;
        try {
            ses = getSessionFactory().openSession();
            con = ses.connection();
            stmt = con.createStatement();
            ResultSet rst = stmt.executeQuery(strSql);
            for (int i = 0; rst.next(); i++) {
                getData(rst, rstList, i);
            }
            rst.close();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (stmt != null) {
                    stmt.close();
                }
                if (ses != null) {
                    ses.close();
                }
            } catch (Exception e) {}
        }
        return rstList;
    }

    private void getData(ResultSet rs, List rstList, int i) {
        List rowList = new ArrayList();
        try {
            ResultSetMetaData rsmd = rs.getMetaData();
            if (i == 0) {
                List colList = new ArrayList();
                for (int k = 1; k <= rsmd.getColumnCount(); k++) {
                    colList.add(new Integer(rsmd.getColumnType(k)));
                }
                rstList.add(colList);
            }
            for (int j = 1; j <= rsmd.getColumnCount(); j++) {
                switch (rsmd.getColumnType(j)) {
                case -7:
                    rowList.add(new Boolean(rs.getBoolean(j)));
                    break;
                case -5:
                    rowList.add(new Long(rs.getLong(j)));
                    break;
                case 1:
                    rowList.add(rs.getString(j));
                    break;
                case 91:
                    rowList.add(rs.getDate(j));
                    break;
                case 3:
                    rowList.add(rs.getBigDecimal(j));
                    break;
                case 8:
                    rowList.add(new Double(rs.getDouble(j)));
                    break;
                case 4:
                    rowList.add(new Integer(rs.getInt(j)));
                    break;
                case 2:
                    rowList.add(new Double(rs.getDouble(j)));
                    break;
                case 7:
                    rowList.add(new Float(rs.getFloat(j)));
                    break;
                case 5:
                    rowList.add(new Short(rs.getShort(j)));
                    break;
                case 92:
                    rowList.add(rs.getDate(j));
                    break;
                case 93:
                    rowList.add(rs.getTimestamp(j));
                    break;
                case -6:
                    rowList.add(new Short(rs.getShort(j)));
                    break;
                case 12:
                    rowList.add(rs.getString(j));
                    break;
                default:
                    rowList.add("");
                    break;
                }
            }
        } catch (Exception e) {}
        rstList.add(rowList);
    }

    public Object getArray(List rstList, int row, int col) {
        Object obj = null;
        if (rstList != null && rstList.size() > 0) {
            obj = ((List) rstList.get(row)).get(col - 1);
            if (obj == null) {
                int intType = ((Integer) ((List) rstList.get(0)).get(col - 1)).
                              intValue();
                switch (intType) {
                case Types.BIT:
                    obj = new Boolean(false);
                    break;
                case Types.BIGINT:
                    obj = new Long(0);
                    break;
                case Types.CHAR:
                    break;
                case Types.DATE:
                    break;
                case Types.DECIMAL:
                    obj = new BigDecimal(0);
                    break;
                case Types.DOUBLE:
                    obj = new Double(0);
                    break;
                case Types.INTEGER:
                    obj = new Integer(0);
                    break;
                case Types.NUMERIC:
                    obj = new Double(0);
                    break;
                case Types.REAL:
                    obj = new Float(0);
                    break;
                case Types.SMALLINT:
                    obj = new Integer(0);
                    break;
                case Types.TIME:
                    break;
                case Types.TIMESTAMP:
                    break;
                case Types.TINYINT:
                    obj = new Integer(0);
                    break;
                case Types.VARCHAR:
                    obj = "";
                    break;
                default:
                    obj = null;
                }
            }
        }
        return obj;
    }
}
